Repository navigation
Conversation
This comment has been minimized.
This comment has been minimized.
9862713 to
24a9990
Compare
|
Changing back to draft. Will make the change in serverless-components and make |
|
Publishing this again since the |
There was a problem hiding this comment.
🟡 Changes recommended
The bundled wrapper does not support directive lists, and the dependencies remain pinned to a temporary upstream revision.
2 open findings
What changed in this PR
Adds target-specific DD_LOG_LEVEL filtering while preserving default logging behavior.
Changes:
- Parses comma-separated
tracingfilter directives and reports invalid entries. - Adds unit coverage for filtering behavior.
- Updates
serverless-componentsdependencies.
| File | Description |
|---|---|
bottlecap/src/logger.rs |
Documents the cold-start logging target. |
bottlecap/src/bin/bottlecap/main.rs |
Implements and tests directive-based filtering. |
bottlecap/Cargo.toml |
Updates shared component revisions. |
bottlecap/Cargo.lock |
Locks the updated revisions. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Converting to draft again. Will move the helper function to a crate in |
DD_LOG_DEBUG_TARGETS is a comma-separated list of tracing targets that log at debug level while DD_LOG_LEVEL stays at its normal level. For example, DD_LOG_DEBUG_TARGETS=cold_start_duration shows only the cold start step durations. Each entry is parsed on its own. An entry that does not parse, such as "cold_start_duration=trace", is logged as a warning and skipped, because the filter's expect() would otherwise fail startup. At DD_LOG_LEVEL=debug or trace the variable is ignored, since a target directive could only lower trace to debug. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DD_LOG_LEVEL_BY_TARGET takes comma-separated `target=level` entries in the RUST_LOG syntax, so that a target can also get a level other than debug, or be made quieter than DD_LOG_LEVEL. An entry with no level turns on all levels for that target. Entries also override the built-in h2, hyper, reqwest and rustls defaults. Each entry is used as written, so the rule that ignored the variable at DD_LOG_LEVEL=debug or trace no longer applies. Invalid entries are still logged as a warning and skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ther Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop DD_LOG_LEVEL_BY_TARGET and read the same `target=level` directives from DD_LOG_LEVEL, for example `info,cold_start_duration=debug`. This matches the Agent Data Plane and datadog-serverless-compat, which both accept directives in DD_LOG_LEVEL. A plain level still works as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without it, the config crate logs "Invalid log level" at ERROR on each cold start when DD_LOG_LEVEL holds filter directives. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DD_LOG_LEVEL is also read by the Datadog libraries and tracers in the function, and they accept only a plain level, so directives there would change or break their logging. A separate variable that only the extension reads avoids that, and no longer needs the serverless-components change. Restores the code of 2afd657 and documents that targets include dependency module paths, such as `dogstatsd`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each entry must be a target, `=`, and a level name. A level with no target, such as `debug`, used to replace DD_LOG_LEVEL, and a target with no level or a span filter such as `[span]` was also accepted. These are now invalid entries, which the extension logs as a warning and skips. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The parsing and the variable name move to the new crate in serverless-components (DataDog/serverless-components#180), so that other serverless agents can share them. Points all five serverless-components dependencies at the head of #180. Its other changes since the current revision are docs and a lock file bump that the extension does not use. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
776fb27 to
acf98dd
Compare
4a71d02 to
e47df3c
Compare
…stant The crate README of datadog-serverless-logging documents how to set the variable and the prefix match. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| dogstatsd = { git = "https://github.com/DataDog/serverless-components", rev = "e5524046ed1184b32de656e3a86eed91976180f8", default-features = false } | ||
| datadog-fips = { git = "https://github.com/DataDog/serverless-components", rev = "e5524046ed1184b32de656e3a86eed91976180f8", default-features = false } | ||
| datadog-agent-config = { git = "https://github.com/DataDog/serverless-components", rev = "e5524046ed1184b32de656e3a86eed91976180f8", default-features = false } | ||
| datadog-agent-trace-sampler = { git = "https://github.com/DataDog/serverless-components", rev = "e5524046ed1184b32de656e3a86eed91976180f8", default-features = false } | ||
| datadog-serverless-logging = { git = "https://github.com/DataDog/serverless-components", rev = "e5524046ed1184b32de656e3a86eed91976180f8", default-features = false } |
clifordshelton
left a comment
There was a problem hiding this comment.
Thanks for moving the filter logic into a shared crate. Keeping it out of DD_LOG_LEVEL makes sense too, since the tracers and libraries also read that variable. I have two inline comments and two broader ones here.
1. Decide whether DD_LOG_LEVEL_BY_TARGET is for customers or for us
This adds a new environment variable that customers can set, but it isn't documented anywhere they'd see it. The README in serverless-components#180 is written for developers. Before we document it, it's worth noticing what the setting depends on. Besides custom targets like cold_start_duration, it accepts our internal Rust module paths, like bottlecap::traces, and crate names with underscores. Those can change any time we move or rename code. If customers start using them, a refactor on our side would quietly change what their setting does, and nobody would notice.
My suggestion is to treat it as a support and debugging tool for now. Only document the custom targets we add on purpose (cold_start_duration, and later ones like trace_flush_duration), since those are names we can keep stable. Module paths still work, but we don't promote them. That way we can add or rename modules without breaking anyone. It would be good to confirm this with the team before merging, since the name and behavior are hard to change once customers rely on them.
2. Bring back the note about target names
An entry matches every target that starts with its text, so trace_flush=debug would also turn on trace_flush_duration. There was a code comment warning about this, but the "Drop the DD_LOG_LEVEL_BY_TARGET notes" commit removed it, and now it's only in the serverless-components README. Whoever adds the next target will be working in logger.rs, not reading that README. It's better to put the note back next to COLD_START_DURATION in logger.rs, so new target names don't accidentally start with an existing one.
| let env_filter = format!( | ||
| "h2=off,hyper=off,reqwest=off,rustls=off,datadog-trace-mini-agent=off,{log_level:?}", | ||
| ); | ||
| let (env_filter, invalid_entries) = build_env_filter( |
There was a problem hiding this comment.
The base filter turns off h2, hyper, reqwest and rustls for a reason, and with this change a DD_LOG_LEVEL_BY_TARGET entry can turn them back on. The extension reads its own stdout and sends it to Datadog as logs (see the "Be careful adding logs inside the logs pipeline" note in AGENTS.md). These HTTP crates log on every request, and that includes the requests the extension makes to send logs. So with hyper=debug, every flush creates more log lines, which get sent in the next flush, which creates more lines, and so on. That adds a lot of noise to the customer's CloudWatch and Datadog logs, and it costs them money.
Since this is an environment variable customers can set, I'd keep those four crates locked to off. You can skip any entry for them and add it to invalid_entries, so the user gets the same warning as for a typo. The cleanest place for this is probably the crate in serverless-components#180, for example by letting the caller pass a list of targets that can't be changed. That would mean making the change before #180 merges. The overrides_base_filter test that sets h2=debug would then change to check that the entry is rejected.
The merge commit has the same tree as the branch head that this PR used before, so the code does not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… constant A DD_LOG_LEVEL_BY_TARGET entry matches every target that starts with its text. The next target is added in logger.rs, so the warning belongs there, not only in the serverless-components README. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Stacked on #1412. Review that PR first. The new variable is parsed by the crate
datadog-serverless-logging, from DataDog/serverless-components#180.Problem
DD_LOG_LEVEL=debugturns on debug logs from all parts of the extension. In a test, three cold starts at debug level logged 117 debug lines, when only 33 of them were cold start durations. Too many logs can be noisy for users (either customers or Datadog engineers) who just want to debug cold starts.This PR
This PR adds
DD_LOG_LEVEL_BY_TARGET, a comma-separated list of<target>=<level>entries. Each entry sets the log level of one tracing target, andDD_LOG_LEVELstays the level of all other targets. Both are environment variables of the Lambda function. For example:DD_LOG_LEVELinfoDD_LOG_LEVEL_BY_TARGETcold_start_duration=debug,dogstatsd=debugSuppose there are logging statements with target
cold_start_duration, like:Then here are some examples (each row is one scenario):
DD_LOG_LEVELDD_LOG_LEVEL_BY_TARGETinfocold_start_duration=debugdebugcold_start_duration=offinfodogstatsd=debugdogstatsdcrate ofserverless-componentscold_start_duration=debugA target can be a custom target that the logging statement sets, a module path such as
bottlecap::traces, or a crate name such asdogstatsd. An entry matches every target that starts with its text. The README ofdatadog-serverless-logginglists all the kinds of targets.The extension logs a warning for an entry that is not valid, and skips it. Examples are a level with no target such as
debug, which would otherwise replaceDD_LOG_LEVEL, and an unknown level such ascold_start_duration=verbose.Why not
DD_LOG_LEVELThe Datadog libraries and tracers in the function also read
DD_LOG_LEVEL, and they accept only a plain level. For example,datadog-lambda-pythonlogs "Invalid log level" for any other value, anddd-trace-rslogs a parse error. Support for<target>=<level>entries inDD_LOG_LEVELwould need changes in several repositories. These include tracers such asdd-trace-javaanddd-trace-dotnet, which Serverless teams do not own and which run in many environments other than Lambda.Testing
I built an arm64 layer of this branch at
4a71d023and deployed it on a Python 3.13 arm64 function. Since then, the branch was rebased ontomain, changed one code comment, and moved to the merge commit of #180, which has the same code. For each row, I forced 2 cold starts and counted the extension's log lines in CloudWatch. Each cold start logs 11 cold start duration lines.DD_LOG_LEVELDD_LOG_LEVEL_BY_TARGETinfocold_start_duration=debugdebugcold_start_duration=offinfodebug,cold_start_duration=verbose,cold_start_duration=debuginfodogstatsd=debugcold_start_duration=debugIn the third row, the extension ignored
debugandcold_start_duration=verbosewith a warning, anddebugdid not turn on the other debug logs. In the fourth row, all the debug messages come from thedogstatsdcrate. I did not test on amd64 or FIPS builds.🤖 Generated with Claude Code